'''
This file is part of ModeliMark.

ModeliMark is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.

ModeliMark is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with ModeliMark.
If not, see <http://www.gnu.org/licenses/>.

Copyright 2012 Jens Frenkel
'''

from modelimark.wrapper import BaseWrapper
import numbers
import shlex, subprocess
import os
from time import *

import win32api,win32process,win32con

compiler_mode = "FMU"
#compiler_mode = "JMU"

class JModelica(BaseWrapper):
    '''
    classdocs
    '''


    def __init__(self):
        '''
        Constructor
        '''
        # Import the function for compilation of models and the FMUModel class

    def flatten(self, model, files, libs):
        '''
        instructs the compiler to parse the Modelica
        code and return the flat model

        :param model: Name of the Model to flatten.
        :type model: String.

        :param files: all Files of the Model to flatten.
        :type files: list of String.
        '''
        assert(isinstance(model,str))
        assert(isinstance(files,list))
        # commented out JModelica 1.4 imports and replaced with 1.7
        #from jmodelica.fmi import compile_fmu
        #from jmodelica.fmi import FMUModel
        #from jmodelica.compiler import CompilerError
        #from jmodelica.compiler import ModelicaCompiler
        from pymodelica.compiler import compile_fmu,compile_jmu
        from pyfmi.fmi import unzip_fmu, FMUModel, FMUException
        from pymodelica.compiler import ModelicaCompiler
        from pymodelica.compiler import CompilerError

        t1 = clock()

        mc = ModelicaCompiler()
        t2 = clock()
        sr = mc.parse_model(files)
        t3 = clock()
        ir = mc.instantiate_model(sr,model)
        t4 = clock()
        fc = mc.flatten_model(ir)
        t5 = clock()
#        mc.generate_code(fc)
#        t6 = clock()

        #print "parse: " + str(t3-t2)
        #print "instantiate: " + str(t4-t3)
        #print "flatten: " + str(t5-t4)
        #print "code gen: " + str(t6-t5)

        dt = t5-t2

        return None,dt

    def translate(self, model, files, libs):
        '''
        flattens the model and turns it into a state which can be simulated

        :param model: Name of the Model to flatten.
        :type model: String.

        :param files: all Files of the Model to flatten.
        :type files: list of String.
        '''
        assert(isinstance(model,str))
        assert(isinstance(files,list))

        # commented out JModelica 1.4 imports and replaced with 1.7
        #from jmodelica.fmi import compile_fmu
        #from jmodelica.fmi import FMUModel
        #from jmodelica.compiler import CompilerError
        #from jmodelica.compiler import ModelicaCompiler
        from pymodelica.compiler import compile_fmu,compile_jmu
        from pyfmi.fmi import unzip_fmu, FMUModel, FMUException
        from pymodelica.compiler import ModelicaCompiler
        from pymodelica.compiler import CompilerError

        pfiles = list()
        for a in files:
            pfiles.append(a)
            #pfiles.append('%s/%s'%(os.getcwd(),a))

        # Compile model
        if compiler_mode=="FMU":
            t1 = clock()
            fmu_name = compile_fmu(model,pfiles)
        else:
            t1 = clock()
            jmu_name = compile_jmu(model,pfiles,compiler='optimica')

        t2 = clock()
        dt = t2 - t1

        return None,dt

    def simulate(self, model, files, libs, startTime, outputInterval, stopTime):
        '''
        flattens, translates and simulates the model using the standard solver
        and a predefined output interval and stop time

        :param model: Name of the Model to flatten.
        :type model: String.

        :param files: all Files of the Model to flatten.
        :type files: list of String.

        :param startTime: Start Time of the Simulation
        :type startTime: numbers.Real.

        :param outputInterval: Intervall for Output of Results.
        :type outputInterval: numbers.Real.

        :param stopTime: Stop Time of the Simulation
        :type stopTime: numbers.Real.
        '''
        assert(isinstance(model,str))
        assert(isinstance(files,list))
        assert(isinstance(startTime,numbers.Real))
        assert(isinstance(outputInterval,numbers.Real))
        assert(isinstance(stopTime,numbers.Real))

        #from jmodelica.fmi import compile_fmu
        #from jmodelica.fmi import FMUModel
        #from jmodelica.compiler import CompilerError
        #from jmodelica.compiler import ModelicaCompiler
        from pymodelica.compiler import compile_fmu,compile_jmu
        from pyfmi.fmi import unzip_fmu, FMUModel, FMUException
        from pymodelica.compiler import ModelicaCompiler
        from pymodelica.compiler import CompilerError

        pfiles = list()
        for a in files:
            pfiles.append(a)
            #pfiles.append('%s/%s'%(os.getcwd(),a))

        try:
            # Compile model
            if compiler_mode=="FMU":
                t1 = clock()
                fmu_name = compile_fmu(model,pfiles)
            else:
                t1 = clock()
                jmu_name = compile_jmu(model,pfiles,compiler='optimica')


            # Load model
            if compiler_mode=="FMU":
                model = FMUModel(fmu_name)
            else:
                model = JMUModel(jmu_name)

            res = model.simulate(start_time=startTime,final_time=stopTime)
            t2 = clock()

            dt = t2 - t1

            return res, dt
        except (CompilerError):
            pass

        return False

